package com.sweet.imserver.server;

import com.sweet.imserver.db.DBVerticle;
import com.sweet.imserver.model.User;
import com.sweet.imserver.utils.Base64Utils;
import io.netty.util.internal.StringUtil;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;
import io.vertx.core.VertxOptions;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.eventbus.EventBus;
import io.vertx.core.json.JsonObject;
import io.vertx.core.net.NetServerOptions;
import io.vertx.core.net.NetSocket;
import io.vertx.core.parsetools.RecordParser;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.UUID;

public class IMServer extends AbstractVerticle {

  private final Logger logger = LogManager.getLogger(IMServer.class);


  public static void main(String[] args) {
    Vertx.vertx(
      new VertxOptions().setBlockedThreadCheckInterval(999999999999999999L)
    ).deployVerticle(new IMServer());
  }

  int count = 0;

  private final Map<String, NetSocketModel> netSocketMap = new HashMap<>();

  private EventBus eventBus;

  @Override
  public void start(Promise<Void> startPromise) throws Exception {
    super.start();
    eventBus = vertx.eventBus();

    vertx.setPeriodic(10000L, t -> {
      if (netSocketMap.isEmpty()) {
        System.out.println("当前无链接");
        return;
      }
      System.out.println("------------");
      System.out.println(netSocketMap.keySet());
    });
    checkHeartbeat();

    vertx.createNetServer(new NetServerOptions().setTcpKeepAlive(true))
      .connectHandler(conn -> {
        printLog("链接：[" + conn.writeHandlerID() + "]");

        final RecordParser recordParser = RecordParser.newDelimited(MESSAGE_END, res -> {
          messageHandler(new JsonObject(Base64Utils.base64ToString(res.toString())),
            conn);
        });

        conn.handler(recordParser);
        conn.endHandler(v -> endHandler(conn));
        conn.exceptionHandler(ex -> printLog("conn error: " + ex.getMessage()));

      }).listen(9092, s -> {
      if (s.succeeded()) {
        printLog("tcp server ok port: 9092");
        startPromise.complete();
      } else {
        s.cause().printStackTrace();
        startPromise.fail(s.cause());
      }
    });
  }

  private void endHandler(NetSocket conn) {
    printLog("conn[" + conn.writeHandlerID() + "] end");
    netSocketMap.entrySet().removeIf(entry ->
      entry.getValue().netSocket.writeHandlerID()
        .equals(conn.writeHandlerID()));
  }


  private void checkHeartbeat() {
    long periodic = 1000L * 60 * 20;
    vertx.setPeriodic(periodic, t -> {
      long now = System.currentTimeMillis();
      netSocketMap.entrySet()
        .removeIf(entry -> {
          if (now - entry.getValue().time > (periodic)) {
            entry.getValue().netSocket.close();
            return true;
          } else {
            return false;
          }
        });
    });
  }

  private static final String MESSAGE_END = "\r\n";

  private void messageHandler(JsonObject body, NetSocket conn) {
    printLog("msg: " + body);
    if (!body.containsKey("type")) {
      return;
    }

    Integer type = body.getInteger("type");

    switch (type) {
      case -1:
        printLog("心跳消息");
        heartbeatMessageHandler(body.getString("fromId"), conn);
        break;

      case 0:
      case 2:
      case 3:
      case 4:
        textMessageHandler(body, conn);
        break;

      case 1:
    }
    return;
  }

  private void textMessageHandler(JsonObject body, NetSocket conn) {
    NetSocketModel toUser = getToUser(body);
    if (toUser != null) {
//    todo 告诉client 服务器已经收到消息
//    sendSuccessMessage(body.getString("mid"), conn);
      printLog("对方在线，转发消息 " + body);
      toUser.netSocket.write(toBase64Message(body));
    }
  }

  private NetSocketModel getToUser(JsonObject body) {
    String to = body.getString("toId");
    NetSocketModel toUser = netSocketMap.get(to);
    if (toUser == null) {
      printLog("不在线：" + to);
      eventBus.send(DBVerticle.MESSAGE_EVENT_BUS, body);
    }
    return toUser;
  }

  private void sendSuccessMessage(String mid, NetSocket conn) {
    JsonObject jsonObject = new JsonObject()
      .put("type", -2)
      .put("mid", mid)
      .put("fromId", "server");
    conn.write(toBase64Message(jsonObject));
  }


  private String toBase64Message(JsonObject body) {
    return Base64Utils.stringToBase64(body.encode()) + MESSAGE_END;
  }

  private void heartbeatMessageHandler(String from, NetSocket conn) {
    if (StringUtil.isNullOrEmpty(from)) return;
    netSocketMap.put(from, new NetSocketModel(conn));
  }

  private void fileHandler(JsonObject body) {
    if (body.containsKey("file")) {
      byte[] files = body.getBinary("file");
      vertx.fileSystem().writeFile(body.getString("name"), Buffer.buffer(files), r -> {
        if (r.succeeded()) {
          System.out.println(body.getString("name") + " file save ok");
        } else {
          r.cause().printStackTrace();
        }
      });
    }
  }

  static class NetSocketModel {
    private final NetSocket netSocket;
    private final long time;

    public NetSocketModel(NetSocket netSocket) {
      this.netSocket = netSocket;
      this.time = System.currentTimeMillis();
    }

    public NetSocket getNetSocket() {
      return netSocket;
    }

    public long getTime() {
      return time;
    }
  }

  private void printLog(String log) {
    logger.debug(log);
  }
}
